package com.mysimpatico.sqlwrapper;

/**
 * 
 * @author Gabriele Kahlout
 * Class for immutable object Table.
 *
 */
public class Table implements SelectableFrom {

    private final String name;
    private final Column[] allColumns;
    private final Column[] columns;
    private final Column[] primaryKeys;
    private final Column[] foreignKeys;
    private final Column ridColumn =  (SqlWrapper.db.equals(SqlWrapper.vendor.POSTGRES))? new Column(SqlWrapper.rid, SqlWrapper.Type.SERIAL) : new Column(SqlWrapper.rid);
    private final Table refTable;
    private final String condition;

    public Table(final String name, final Column[] columns) {
        this.name = name;
        this.columns = columns;
        this.allColumns = new Column[columns.length + 1];
        this.allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);

        primaryKeys = new Column[]{ridColumn};
        foreignKeys = null;
        refTable = null;
        condition = null;
    }

    public Table(final String name) {
        this.name = name;
        allColumns = new Column[]{ridColumn};
        columns = null;
        primaryKeys = allColumns;
        foreignKeys = null;
        refTable = null;
        condition = null;
    }

    public Table(final String name, final Column column) {
        this(name, new Column[]{column});
    }

    public Table(final String name, final Column column, final Column primaryKey) {
        this(name, new Column[]{column}, new Column[]{primaryKey});
    }

    public Table(final String name, final Column[] columns, final Column[] primaryKeys, final boolean foreignKeys) {
        this.name = name;
        this.columns = columns;
        this.allColumns = new Column[columns.length + 1];
        this.allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        this.primaryKeys = primaryKeys;
        this.foreignKeys = primaryKeys;
        refTable = primaryKeys[0].getRefTable();
        condition = null;
    }

    public Table(final String name, final Column[] columns, final Table refTable, final Column[] primaryKeys) {
        this.name = name;
        this.columns = columns;
        this.allColumns = new Column[columns.length + 1];
        this.allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        this.primaryKeys = primaryKeys;
        foreignKeys = primaryKeys;
        this.refTable = refTable;
        condition = null;

    }

    public Table(final String name, final Column[] columns, final Table refTable, final Column primaryKey) {
        this.name = name;
        this.columns = columns;
        this.allColumns = new Column[columns.length + 1];
        this.allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        primaryKeys = new Column[]{primaryKey};
        foreignKeys = primaryKeys;
        this.refTable = refTable;
        condition = null;

    }

    public Table(final String name, final Column[] columns, final Column[] primaryKeys) {
        this.name = name;
        this.columns = columns;
        allColumns = new Column[columns.length + 1];
        allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        this.primaryKeys = primaryKeys;
        foreignKeys = null;
        refTable = null;
        condition = null;
    }

    public Table(final String name, final Column[] columns, final Column[] primaryKeys, final Table refTable, final Column[] foreignKeys){
        this.name = name;
        this.columns = columns;
        this.allColumns = new Column[columns.length + 1];
        this.allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        this.primaryKeys = primaryKeys;
        this.foreignKeys = foreignKeys;
        this.refTable = refTable;
        condition = null;
    }

    public Table(final String name, final Column[] columns, final Column[] primaryKeys, final String checkQuery) {
        this.name = name;
        this.columns = columns;
        allColumns = new Column[columns.length + 1];
        allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        this.primaryKeys = primaryKeys;
        foreignKeys = null;
        refTable = null;
        condition = checkQuery;
    }

    public Table(final String name, final Column[] columns, final Column primaryKey) {
        this(name, columns, new Column[]{primaryKey});
    }

    public Table(final String name, final Column[] columns, final boolean exists, final String checkQuery) {
        this.name = name;
        this.columns = columns;
        allColumns = new Column[columns.length + 1];
        allColumns[columns.length] = ridColumn;
        System.arraycopy(columns, 0, this.allColumns, 0, columns.length);
        condition = ((exists) ? "" : "NOT") + " EXISTS( " + checkQuery + ")";
        refTable = null;
        primaryKeys = null;
        foreignKeys = null;
    }

    public Table(final String name, final Column col, final boolean exists, String checkQuery) {
        this(name, new Column[]{col}, exists, checkQuery);
    }

    @Override
    public String getName() {
        return name;
    }

    public int size() {
        return allColumns.length;
    }

    public int defsize() {
        int counter = 0;
        for (final Column c : allColumns) {
            if (c.getDef() != null) {
                counter++;
            }
        }
        /*else if (c.refField != null)
        if (((Column) c).refField.getDef() != null)
        counter++;
        }*/
        return counter;
    }

    static String getOnDelete(){
        return " ON DELETE CASCADE";
    }

    private String getForeign() {
        final String foreign;
        return (foreignKeys != null) ? ", FOREIGN KEY (" + (foreign = SqlWrapper.joinColumns(foreignKeys, SqlWrapper.QueryType.FOREIGN)) + ") REFERENCES " + refTable.getName() + " (" + foreign + ") " + getOnDelete() : "";
    }

    private String getPrimaryKey() {
        if (primaryKeys == null) return "";
        final boolean sqlite = SqlWrapper.db.equals(SqlWrapper.vendor.SQLITE);
        String ret = ", ";
        if (sqlite) ret += "UNIQUE (" + SqlWrapper.joinColumns(primaryKeys, SqlWrapper.QueryType.FOREIGN) + ") CHECK(" + SqlWrapper.joinColumns(primaryKeys, SqlWrapper.QueryType.PRIMARY) + ")";
        else ret += "PRIMARY KEY(" + SqlWrapper.joinColumns(primaryKeys, SqlWrapper.QueryType.FOREIGN) + ")";
        return ret;
    }

    private String getCondition() {
        return (condition == null) ? "" : ", CHECK( " + condition + ")";
    }

    @Override
	public final String toString(){
	 final String cols = SqlWrapper.joinColumns(allColumns, SqlWrapper.QueryType.CREATE);
        return name + " (" + cols + getPrimaryKey() + getForeign() + getCondition() + ")";	
        /* if (foreignKeys) return name + " (" + SqlWrapper.joinColumns(allColumns,SqlWrapper.QueryType.CREATE) + ", FOREIGN KEY (" + (foreign = SqlWrapper.joinColumns(primaryKeys,SqlWrapper.QueryType.FOREIGN)) + ") REFERENCES " +  refTable.getName() + " ("+foreign+ "))";
        return (primaryKeys == null)? name + " (" + SqlWrapper.joinColumns(allColumns,SqlWrapper.QueryType.CREATE) + ")":
        name + " (" + SqlWrapper.joinColumns(allColumns,SqlWrapper.QueryType.CREATE) + ", UNIQUE ("  + SqlWrapper.joinColumns(primaryKeys,SqlWrapper.QueryType.FOREIGN)+ ") CHECK("+SqlWrapper.joinColumns(primaryKeys, QueryType.PRIMARY) + "))"; */
    }

    @Override
    public Column[] getColumns() {
        return columns;
    }

    public Column[] getAllColumns() {
        return allColumns;
    }

    @Override
    public Column getIdColumn() {
        return ridColumn;
    }

    @Override
    public boolean supportBoolean() {
        for (Column c : allColumns) {
            if (c.getType().equals(SqlWrapper.Type.BOOLEAN)) {
                return true;
            }
        }
        return false;
    }
}
